using CfgTable;
using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.GameAOT;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;

namespace IQIGame.Onigao.GamePlay
{
    /// <summary>
    /// Map管理器
    /// 1.处理不同Area，不同region的Map创建
    /// 2.缓存Symbol实例，在不同的Map之间复用
    /// </summary>
    public partial class UIMapManager : Singleton<UIMapManager>
    {
        #region Properties

        private GameObject root { get; }

        #endregion

        #region Constructor

        private UIMapManager()
        {
            root = new GameObject
            {
                name = "Map"
            };
            root.transform.SetParent(ManagerCenter.UI.UIRoot);
            resLoader = ResLoader.Create();
            _maps = new Dictionary<int, UIMapRoot>();

            InitPools();
            InitFogPool();
        }

        private void InitPools()
        {
            var defaultTemplate = resLoader.LoadABAsset<GameObject>(FrameworkPath.GetUIPrefabPath("Map/MapSymbol"));
            var defaultPool = new MapSymbolPool<UINormalMapSymbol>(defaultTemplate);
            var types = Enum.GetValues(typeof(MapUiEntityType));
            _pools = new Dictionary<MapUiEntityType, BaseMapSymbolPool>();
            for (var i = 0; i < types.Length; i++)
            {
                _pools[(MapUiEntityType)types.GetValue(i)] = defaultPool;
            }

            _pools[MapUiEntityType.Quest] = new MapSymbolPool<UIQuestMapSymbol>(defaultTemplate);
            _pools[MapUiEntityType.QuestArea] = new MapSymbolPool<UIQuestAreaSymbol>(defaultTemplate);
            _pools[MapUiEntityType.Portal] = new MapSymbolPool<UIPortalMapSymbol>(defaultTemplate);
            _pools[MapUiEntityType.QuestTarget] = new MapSymbolPool<UIQuestTargetSymbol>(defaultTemplate);
            _pools[MapUiEntityType.Tracer] = new MapSymbolPool<UIEntityTracerSymbol>(defaultTemplate);
        }

        private void InitFogPool()
        {
            var template = resLoader.LoadABAsset<GameObject>(FrameworkPath.GetUIPrefabPath("Map/DefaultMapSymbol"));
            var fogPool = new MapSymbolPool<UIFogMapSymbol>(template);
            _pools[MapUiEntityType.Fog] = fogPool;
        }

        #endregion

        public ResLoader resLoader { get; }

        public UIMapRoot GetMap(int regionId)
        {
            return _maps.GetValueOrDefault(regionId);
        }

        public async UniTask<UIMapRoot> LoadMap(int regionId)
        {
            if (_maps.TryGetValue(regionId, out var map))
            {
                return map;
            }

            map = new UIMapRoot(regionId);
            var mapAsset =
                await map.resLoader.LoadABAssetAwait<GameObject>(map.config.MapPath_fullPath);
            map.Init(mapAsset.Instantiate());
            _maps.Add(regionId, map);
            return map;
        }

        public void UnloadMap(int regionId)
        {
            if (!_maps.TryGetValue(regionId, out var map))
            {
                return;
            }

            var mapGo = map.gameObject;
            map.Dispose();
            Object.Destroy(mapGo);
            _maps.Remove(regionId);
        }

        private UIBaseMapSymbol GetOne(IMapSymbolData data)
        {
            if (data?.config == null)
            {
                return null;
            }

            if (!_pools.TryGetValue(data.config.Type, out var pool))
            {
                Debug.LogError($"No pool for {data.config.Type}");
                return null;
            }

            var symbol = pool.GetOne();
            symbol.data = data;
            return symbol;
        }

        private void PutOne(UIBaseMapSymbol symbol)
        {
            if (!_pools.TryGetValue(symbol.data.config.Type, out var pool))
            {
                Debug.LogError($"No pool for {symbol.data.config.Type}");
                return;
            }

            pool.PutOne(symbol);
        }

        public UIBaseMapSymbol GetSymbol(object rawData, int specifiedCid = 0)
        {
            var symbolData = GetSymbolData(rawData);
            if (specifiedCid != 0)
            {
                symbolData.config = TableCenter.mapUiEntity.Get(specifiedCid);
                symbolData.layer += symbolData.config.DynamicLayer;
            }
            return GetOne(symbolData);
        }

        public void PutSymbol(UIBaseMapSymbol symbol)
        {
            PutOne(symbol);
            PutSymbolData(symbol.data);
        }

        public void SetSelection(UIBaseMapSymbol target)
        {
            if (_selection != null)
            {
                PutSymbol(_selection);
                _selection = null;
            }

            if (target == null)
            {
                return;
            }

            var symbolData = GetSymbolData(target.data.GetRawData());
            symbolData.config = TableCenter.mapUiEntity.DataList.First(cfg => cfg.Type == MapUiEntityType.Selection);
            symbolData.layer += symbolData.config.DynamicLayer;
            _selection = GetOne(symbolData);

            _selection.owner = target.owner;
            var layer = target.data.layer;
            _selection.transform.SetParent(target.owner.GetLayer(layer).root.transform);
            _selection.transform.SetAsFirstSibling();
            _selection.size = target.size * 1.2f;
            _selection.Show();
            _selection.transform.localScale = Vector3.one;
            _selection.transform.localPosition = target.transform.localPosition;
        }

        private static IMapSymbolData GetSymbolData(object rawData)
        {
            IMapSymbolData symbolData = null;
            switch (rawData)
            {
                case TViewEntity entity:
                    symbolData = ClassPool.Get<EntityMapSymbolData>();
                    symbolData.SetRawData(entity);
                    break;
                case MainUIEntityData uiData:
                    symbolData = ClassPool.Get<BriefMapSymbolData>();
                    symbolData.SetRawData(uiData);
                    break;
            }

            return symbolData;
        }

        private static void PutSymbolData(IMapSymbolData rawData)
        {
            switch (rawData)
            {
                case BriefMapSymbolData simpleMapSymbolData:
                    ClassPool.Put(simpleMapSymbolData);
                    break;
                case EntityMapSymbolData entityMapSymbolData:
                    ClassPool.Put(entityMapSymbolData);
                    break;
            }
        }

        private readonly Dictionary<int, UIMapRoot> _maps;
        private Dictionary<MapUiEntityType, BaseMapSymbolPool> _pools;
        private UIBaseMapSymbol _selection;
    }
}